package org.kalai.unifypay.tfb.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

public class RequestUtil {

	// 签名
	private static String sign;

	// 服务端返回的数据
	private static String responseData;

	/**
	 * 发起请求
	 * 
	 * @param url
	 * @param param
	 *            参数列表（按首字母进行排序）
	 */
	public static Map<String, String> sendRequst(final String url, TreeMap<String, String> param) {

		Map<String, String> map = new HashMap<String, String>();
		StringBuffer paramstr = new StringBuffer();
		for (String pkey : param.keySet()) {
			String pvalue = param.get(pkey);
			if (null != pvalue && "" != pvalue) {// 空值不传递，不签名
				paramstr.append(pkey + "=" + pvalue + "&"); // 签名原串，不url编码
			}
		}
		String paramSrc = paramstr.substring(0, paramstr.length() - 1);
		sign = sign(paramSrc);
		paramstr.append("sign=" + sign);
		System.out.println(paramstr);
		String cipherData = encrypt(paramstr.toString());
		responseData = doPost(url, "cipher_data=" + URLEncoder.encode(cipherData));
		Map<String, String> mapRes =parseXml(responseData);
		String cipherResponseData = null;
		String responseData = null;
		if(mapRes.containsKey("cipher_data")){
			cipherResponseData = mapRes.get("cipher_data");
		}
		if (StringUtils.isNoneBlank(cipherResponseData)) {
			responseData = decryptResponseData(cipherResponseData);
			String sign = responseData.substring(responseData.indexOf("sign=") + 5, responseData.length());
			String source = responseData.substring(0, responseData.lastIndexOf("&sign"));
			// verify(source, sign); // 验证签名
			StrToMap(source, map);
			if (map.containsKey("tfb_serialno")) {
				map.put("respCode", "00");
				map.put("respDesc", "success");
			} else {
				map.put("respCode", "01");
				map.put("respDesc", "fail");
			}
		} else {
			map.put("respCode", "01");
			map.put("respDesc", "fail");
			map.put("retcode", mapRes.get("retcode"));
			map.put("retmsg", mapRes.get("retmsg"));
		}

		return map;
	}

	/**
	 * 将返回数据解析成map
	 * source:serialno_desc=交易成功&serialno_state=1&sp_serialno=20161020021139&
	 * spid=1800046674&tfb_rsptime=20161020141212&tfb_serialno=2016102000005237
	 * 
	 * @author nicholas
	 * @param content
	 * @param map
	 */
	private static void StrToMap(String content, Map<String, String> map) {
		String[] str = content.split("&");
		if (str.length > 0) {
			for (String kv : str) {
				String[] strKv = kv.split("=");
				String key = strKv[0];
				String val = strKv[1];
				map.put(key, val);
			}
		}

	}

	/**
	 * 验签
	 * 
	 * @param source
	 *            签名内容
	 * @param sign
	 *            签名值
	 * @return
	 */
	private static boolean verify(String source, String sign) {
		String publicKey = RSAUtils.loadPublicKey(RSAUtils.GC_PUBLIC_KEY_PATH);
		try {
			boolean verifyResult = RSAUtils.verify(source.getBytes(), publicKey, sign);
			if (verifyResult) {
				System.out.println("验证签名通过");
			} else {
				System.out.println("签名有误");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 对原串进行签名
	 * 
	 * @param paramSrc
	 *            the source to be signed
	 * @return
	 */
	private static String sign(String paramSrc) {
		StringBuffer strbuff = new StringBuffer();
		strbuff.append(paramSrc + "&key=" + RSAUtils.MD5_KEY);
		String sign = null;
		try {
			sign = RSAUtils.getMD5Str(strbuff.toString());
			System.out.println("签名:" + sign);
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		return sign;
	}

	/**
	 * 加密得到cipherData
	 * 
	 * @param paramstr
	 * @return
	 */
	private static String encrypt(String paramstr) {
		String publickey = RSAUtils.loadPublicKey(RSAUtils.GC_PUBLIC_KEY_PATH);

		String cipherData = null;
		try {
			cipherData = RSAUtils.encryptByPublicKey(paramstr.getBytes(), publickey);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return cipherData;
	}

	/**
	 * rsa解密
	 * 
	 * @param cipherData
	 *            the data to be decrypt
	 * @return
	 */
	private static String decryptResponseData(String cipherData) {
		String privatekey = RSAUtils.loadPrivateKey(RSAUtils.PRIVATE_KEY_PATH);
		String result;
		try {
			result = RSAUtils.decryptByPrivateKey(Base64.decode(cipherData), privatekey);
			result = new String(result.getBytes("ISO-8859-1"), "utf-8");
			System.out.println("解密结果:" + result);
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private static Map<String, String> parseXml(String responseData) {
		Map<String, String> map = new HashMap<String, String>();
		try {
			Document dom = DocumentHelper.parseText(responseData);
			Element root = dom.getRootElement();
			if (root.element("cipher_data") != null) {
				String cipher_data = root.element("cipher_data").getText();
				map.put("cipher_data", cipher_data);
			}
			if (root.element("retmsg") != null) {
				String retmsg = root.element("retmsg").getText();
				map.put("retmsg", retmsg);
			}
			if (root.element("retcode") != null) {
				String retcode = root.element("retcode").getText();
				map.put("retcode", retcode);
			}
		} catch (DocumentException e1) {
			e1.printStackTrace();
		}
		return map;
	}

	public static String doPost(String url, String param) {
		PrintWriter out = null;
		BufferedReader in = null;
		String result = "";
		try {
			URL realUrl = new URL(url);
			// 打开和URL之间的连接
			URLConnection conn = realUrl.openConnection();
			// 设置通用的请求属性
			conn.setRequestProperty("accept", "*/*");
			conn.setRequestProperty("connection", "Keep-Alive");
			conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			conn.setRequestProperty("content-type", "application/x-www-form-urlencoded;charset=UTF-8");
			// 发送POST请求必须设置如下两行
			conn.setDoOutput(true);
			conn.setDoInput(true);
			// 获取URLConnection对象对应的输出流
			out = new PrintWriter(conn.getOutputStream());
			// 发送请求参数
			out.print(param);
			// flush输出流的缓冲
			out.flush();
			// 定义BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}
			System.out.println("请求结果:" + result);
		} catch (Exception e) {
			System.out.println("发送 POST 请求出现异常！" + e);
			e.printStackTrace();
		}
		// 使用finally块来关闭输出流、输入流
		finally {
			try {
				if (out != null) {
					out.close();
				}
				if (in != null) {
					in.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		return result;
	}
}
